package com.bbc.yuntun.platform.service.wx;

import com.alibaba.fastjson2.JSONObject;
import com.bbc.yuntun.platform.domain.model.WxLoginBody;
import com.bbc.yuntun.platform.domain.model.WxLoginUser;
import com.bbc.yuntun.platform.domain.WxUser;
import com.bbc.yuntun.platform.service.admin.WxUserService;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Security;

/**
 * 微信登录Service
 *
 * @author Wyy
 * @date 2022/7/5
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WxLoginService {
    @Resource
    private RestTemplate restTemplate;

    @Value("${wx.appId}")
    private String appId;

    @Value("${wx.appSecret}")
    private String appSecret;

    private final WxTokenService wxTokenService;

    private final WxUserService wxUserService;


    /**
     * 授权
     */
    private JSONObject authWx(WxLoginBody loginBody) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" +
                appId + "&secret=" + appSecret + "&js_code=" + loginBody.getLoginCode() + "&grant_type=authorization_code";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> entity = new HttpEntity<String>(headers);
        String strBody = restTemplate.exchange(url, HttpMethod.GET, entity, String.class).getBody();
        JSONObject jsonObject = JSONObject.parseObject(strBody);
        if (jsonObject == null || jsonObject.getIntValue("errcode") != 0) {
            log.error("微信用户登录失败，接口返回结果：{}", jsonObject);
            return null;
        } else {
            return jsonObject;
        }
    }

    /**
     * 判断用户是否存在 不存在则注册用户信息
     *
     * @param loginBody 微信登录主体
     * @return token
     */
    public String wxUserLogin(WxLoginBody loginBody) throws Exception {
        JSONObject jsonObject = authWx(loginBody);
        String openId = jsonObject.getString("openid");
        String sessionKey = jsonObject.getString("session_key");
        WxUser wxUser = wxUserService.selectWxUserByOpenId(openId);
        if (null == wxUser) {
            wxUser = new WxUser();
            wxUser.setOpenId(openId);
        }
        wxUser.setSessionKey(sessionKey);
        wxUser.setNickName(loginBody.getNickName());
        wxUser.setSex(loginBody.getSex());
        // 解密手机号码
        /*String decryptStr = wxDecrypt(loginBody.getEncryptedData(), loginBody.getIv(), sessionKey);
        if (StringUtils.isEmpty(decryptStr)) {
            log.error("解密手机号码失败");
            return null;
        }
        JSONObject res = JSONObject.parseObject(decryptStr);
        String mobile = res.getString("phoneNumber");
        if (StringUtils.isEmpty(mobile)) {
            log.error("解密手机号码失败");
            return null;
        }
        wxUser.setMobile(mobile);*/
        WxLoginUser wxLoginUser = new WxLoginUser();
        wxLoginUser.setWxUser(wxUser);
        // 保存或更新微信用户信息并创建token
        wxUserService.saveOrUpdate(wxUser);
        return wxTokenService.createToken(wxLoginUser);
    }

    /**
     * 微信 数据解密 <br/>
     * 对称解密使用的算法为 AES-128-CBC，数据采用PKCS#7填充 <br/>
     * 对称解密的目标密文:encrypted=Base64_Decode(encryptData) <br/>
     * 对称解密秘钥:key = Base64_Decode(session_key),aeskey是16字节 <br/>
     * 对称解密算法初始向量:iv = Base64_Decode(iv),同样是16字节 <br/>
     *
     * @param encrypted 目标密文
     * @param session_key 会话ID
     * @param iv 加密算法的初始向量
     */
    private String wxDecrypt(String encrypted, String iv, String session_key) {
        String result = null;
        byte[] encrypted64 = Base64.decodeBase64(encrypted);
        byte[] key64 = Base64.decodeBase64(session_key);
        byte[] iv64 = Base64.decodeBase64(iv);
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            KeyGenerator.getInstance("AES").init(128);
            result = new String(decrypt(encrypted64, key64, generateIV(iv64)));
        } catch (Exception e) {
            log.error("解密手机号失败！{}", e.getMessage());
        }
        return result;
    }

    /**
     * * 生成iv
     */
    private AlgorithmParameters generateIV(byte[] iv) throws Exception {
        // iv 为一个 16 字节的数组，这里采用和 iOS 端一样的构造方法，数据全为0
        // Arrays.fill(iv, (byte) 0x00);
        AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
        params.init(new IvParameterSpec(iv));
        return params;
    }

    /**
     * * 生成解密
     */
    private byte[] decrypt(byte[] encryptedData, byte[] keyBytes, AlgorithmParameters iv) throws Exception {
        Key key = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        // 设置为解密模式
        cipher.init(Cipher.DECRYPT_MODE, key, iv);
        return cipher.doFinal(encryptedData);
    }

}
